/* eslint-env node */
import {existsSync, unlinkSync} from 'node:fs';
import fs from 'node:fs/promises';
import path from 'node:path';

// joining path of directory
const tmpOutputPath = path.join(
  __dirname,
  '../static/app/constants/ios-device-list.tmp.tsx'
);
const outputPath = path.join(__dirname, '../static/app/constants/ios-device-list.tsx');
const directoryPath = path.join(__dirname, '../node_modules/ios-device-list/');

type Generation = string;
type Identifier = string;
type Mapping = Record<Identifier, Generation>;

async function getDefinitionFiles(): Promise<string[]> {
  const files: string[] = [];

  const maybeJSONFiles = await fs.readdir(directoryPath);

  // listing all files using forEach
  for (const file of maybeJSONFiles) {
    if (!file.endsWith('.json') || file === 'package.json') {
      continue;
    }

    files.push(path.join(path.resolve(directoryPath), file));
  }

  return files;
}

async function collectDefinitions(files: string[]): Promise<Mapping> {
  const definitions: Mapping = {};

  const queue = [...files];

  while (queue.length > 0) {
    const file = queue.pop();

    if (!file) {
      throw new Error('Empty queue');
    }

    const contents = await fs.readFile(file, 'utf8');
    const content = JSON.parse(contents);

    if (typeof content?.[0]?.Identifier === 'undefined') {
      continue;
    }

    for (let i = 0; i < content.length; i++) {
      if (content[i].Identifier) {
        definitions[content[i].Identifier] = content[i].Generation;
      }
    }
  }

  return definitions;
}

const HEADER = `
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
// generated using scripts/extract-ios-device-names.ts as part of build step.
// the purpose of the script is to extract only the iOS information that Sentry cares about
// and discard the rest of the JSON so we do not end up bloating bundle size.
`;

// The formatting issues will be picked up by the CI and can quickly be fixed by
// running `yarn fix` command that triggers the linter and formatter.
const template = (contents: string) => {
  return `
      ${HEADER}
      const iOSDeviceMapping: Record<string, string> = ${contents}

      export {iOSDeviceMapping}
  `;
};

async function run() {
  const files = await getDefinitionFiles();
  const definitions = await collectDefinitions(files);
  const formatted = template(JSON.stringify(definitions, undefined, 2));

  // All exit code has to synchronous
  function cleanup() {
    if (existsSync(tmpOutputPath)) {
      unlinkSync(tmpOutputPath);
    }
  }

  process.once('exit', cleanup);
  process.once('SIGINT', () => {
    cleanup();
    process.exit(1);
  });

  // Write to tmp output path
  await fs.writeFile(tmpOutputPath, formatted);
  // Rename the file (atomic)
  await fs.rename(tmpOutputPath, outputPath);
}

run()
  // eslint-disable-next-line no-console
  .catch(error => console.error(`Failed to run extract-ios-device-names`, error));
